﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Runtime.InteropServices;
using System.IO;
using System.Diagnostics;

namespace WSBackup
{
    class Util
    {
        [return: MarshalAs(UnmanagedType.Bool)]
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer); //Used to use ref with comment below

        /// <summary>
        /// contains information about the current state of both physical and virtual memory, including extended memory
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public class MEMORYSTATUSEX
        {
            /// <summary>
            /// Size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx. 
            /// </summary>
            public uint dwLength;

            /// <summary>
            /// Number between 0 and 100 that specifies the approximate percentage of physical memory that is in use (0 indicates no memory use and 100 indicates full memory use). 
            /// </summary>
            public uint dwMemoryLoad;

            /// <summary>
            /// Total size of physical memory, in bytes.
            /// </summary>
            public ulong ullTotalPhys;

            /// <summary>
            /// Size of physical memory available, in bytes. 
            /// </summary>
            public ulong ullAvailPhys;

            /// <summary>
            /// Size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead. 
            /// </summary>
            public ulong ullTotalPageFile;

            /// <summary>
            /// Size of available memory to commit, in bytes. The limit is ullTotalPageFile. 
            /// </summary>
            public ulong ullAvailPageFile;

            /// <summary>
            /// Total size of the user mode portion of the virtual address space of the calling process, in bytes. 
            /// </summary>
            public ulong ullTotalVirtual;

            /// <summary>
            /// Size of unreserved and uncommitted memory in the user mode portion of the virtual address space of the calling process, in bytes. 
            /// </summary>
            public ulong ullAvailVirtual;

            /// <summary>
            /// Size of unreserved and uncommitted memory in the extended portion of the virtual address space of the calling process, in bytes. 
            /// </summary>
            public ulong ullAvailExtendedVirtual;

            /// <summary>
            /// Initializes a new instance of the <see cref="T:MEMORYSTATUSEX"/> class.
            /// </summary>
            public MEMORYSTATUSEX()
            {
                this.dwLength = (uint)Marshal.SizeOf(typeof(Util.MEMORYSTATUSEX));
            }
        }

        private static int m_log = 0;

        public static void Loguj(string message)
        {
            Debug.WriteLine(m_log.ToString() + " " + message);
            m_log++;
        }

        public static ulong GetAvailVirtual()
        {
            var ms = new MEMORYSTATUSEX();
            var ok = GlobalMemoryStatusEx(ms);
            return ms.ullAvailVirtual;
        }
        
        public static bool CompareStream(Stream a, Stream b, OnProgress eh)
        {
            var retval = true;

            var a_pos = a.Position;
            var b_pos = b.Position;

            if (a.Length != b.Length)
                return false;

            a.Position = 0;
            b.Position = 0;

            var a_buffer = new byte[FileArchiver.buffer_length];
            var b_buffer = new byte[FileArchiver.buffer_length];

            var bytes = 0;

            var last = DateTime.Now.AddMilliseconds(100);
            var total = a.Length;
            var current = a.Position;

            while ((bytes = a.Read(a_buffer, 0, FileArchiver.buffer_length)) > 0)
            {
                b.Read(b_buffer, 0, FileArchiver.buffer_length);

                if (!a_buffer.SequenceEqual(b_buffer))
                {
                    retval = false;
                    break;
                }

                if (eh != null && DateTime.Now > last)
                {
                    eh(string.Format("Comparing {0} / {1} {2:P2}", Util.FormatBytes(current), Util.FormatBytes(total), (float)current / total));
                    last = DateTime.Now.AddMilliseconds(250);
                }

                current += bytes;
            }

            a.Position = a_pos;
            b.Position = b_pos;

            return retval;
        }

        public static string FormatBytes(long len)
        {
            var length = Convert.ToSingle(len);

            int order = 0;
            while (length >= 1024 && order < 3)
            {
                order++;
                length /= 1024;
            }

            // Adjust the format string to your preferences. For example "{0:0.#}{1}" would show a single decimal place, and no space.
            if (order == 0)
                return String.Format("{0:N0} B", len);

            return String.Format("{0:N2} {1}", length, new[] { "KB", "MB", "GB", "TB" }[order - 1]);
        }
    }
}
